package cn.example.gate.protocolBuf;

import cn.example.common.protocol.StructUtil;
import cn.hutool.core.map.MapUtil;
import io.netty.buffer.ByteBuf;
import org.artifact.core.constant.ArtifactErrorCode;
import org.artifact.core.context.bytebuf.NettyByteBuf;
import org.artifact.core.context.bytebuf.ProxyContent;
import org.artifact.core.exception.ArtifactExceptionUtil;
import org.artifact.core.lang.DTO;
import org.artifact.core.lang.ISerializable;

import java.util.*;

/**
 * @author : SandKing
 * @CreateDate : 2019/8/6 10:51
 * @Description ：Please describe this document
 */
public class ProtocolBuf extends NettyByteBuf {
    static final int STRUCT_CODE = 183928832;

    public ProtocolBuf(ByteBuf buf) {
        super(buf);
    }

    @Override
    public void writeMap(Map value) {
        if (value == null) {
            writeNull();
            return;
        }
        Byte _byte = ProxyContent.MAP_SUB_TYPE.getByKey(value.size());
        if (_byte != null) {
            onWriteByte(_byte);
        } else {
            onWriteByte(ProxyContent.MAP);
            writeInt(value.size());
        }

        Set<Map.Entry> entrys = value.entrySet();
        for (Map.Entry entry : entrys) {
            writeObject(entry.getKey());
            writeObject(entry.getValue());
        }
    }

    @Override
    public void writeCollection(Collection value) {
        if (value == null) {
            writeNull();
            return;
        }
        Byte _byte = ProxyContent.COLLECTION_SUB_TYPE.getByKey(value.size());
        if (_byte != null) {
            onWriteByte(_byte);
        } else {
            onWriteByte(ProxyContent.COLLECTION);
            writeInt(value.size());
        }
        for (Object object : value) {
            writeObject(object);
        }
    }

    public void writeObject(Object value) {
        if (value == null) {
            writeNull();
        } else if (value instanceof Byte) {
            writeByte((byte) value);
        } else if (value instanceof Short) {
            writeShort((short) value);
        } else if (value instanceof Integer) {
            writeInt((int) value);
        } else if (value instanceof Long) {
            writeLong((long) value);
        } else if (value instanceof Float) {
            writeFloat((float) value);
        } else if (value instanceof Double) {
            writeDouble((double) value);
        } else if (value instanceof Boolean) {
            writeBoolean((boolean) value);
        } else if (value instanceof String) {
            writeString((String) value);
        } else if (value instanceof Date) {
            writeDate((Date) value);
        } else if (value instanceof Map) {
            writeMap((Map) value);
        } else if (value instanceof DTO) {
            Map<Object,Object> out = new HashMap<>();
            ((DTO) value).toMap(out);
            ArtifactExceptionUtil.isTrue(0,out.containsKey(STRUCT_CODE));
            out.put(STRUCT_CODE, StructUtil.structCode(value.getClass()));
            writeMap(out);
        } else if (value instanceof Collection) {
            writeCollection((Collection<Object>) value);
        } else if (value instanceof ISerializable) {
            writeSerializable((ISerializable) value);
        } else if (value instanceof Enum) {
            writeEnum((Enum<?>) value);
        }
    }

    @Override
    public Map readMap() {
        byte tag = getTag();

        int size = 0;
        switch (tag) {
            case ProxyContent.NULL:
                return readNull();
            case ProxyContent.MAP:
                skipBytes(Byte.BYTES);
                size = readInt();
            default:
                Integer _int = ProxyContent.MAP_SUB_TYPE.getbyValue(tag);
                ArtifactExceptionUtil.isTrue(ArtifactErrorCode.UNKNOWN_ERROR, _int == null);
                skipBytes(Byte.BYTES);
                size = _int;
        }

        Map map = new HashMap<>();
        for (int i = 0; i < size; i++) {
            map.put(readObject(), readObject());
        }
        return map;
    }

    @Override
    public Collection<?> readCollection() {
        byte tag = getTag();
        int size = 0;
        switch (tag) {
            case ProxyContent.NULL:
                return readNull();
            case ProxyContent.COLLECTION:
                skipBytes(Byte.BYTES);
                size = readInt();
            default:
                Integer _int = ProxyContent.COLLECTION_SUB_TYPE.getbyValue(tag);
                ArtifactExceptionUtil.isTrue(ArtifactErrorCode.UNKNOWN_ERROR, _int == null);
                skipBytes(Byte.BYTES);
                size = _int;
        }

        Collection list = new ArrayList<>();
        for (int i = 0; i < size; i++) {
            list.add(readObject());
        }
        return list;
    }

    public Object readObject() {
        byte tag = getTag();
        if (tag == ProxyContent.NULL) {
            return readNull();
        } else if (tag == ProxyContent.BYTE) {
            return readByte();
        } else if (tag == ProxyContent.SHORT_8 || tag == ProxyContent.SHORT_16) {
            return readShort();
        } else if (ProxyContent.INT_SUB_TYPE.containsValue(tag) || tag == ProxyContent.INT_8 || tag == ProxyContent.INT_16 || tag == ProxyContent.INT_32) {
            return readInt();
        } else if (tag == ProxyContent.LONG_8 || tag == ProxyContent.LONG_16 || tag == ProxyContent.LONG_32 || tag == ProxyContent.LONG_64) {
            return readLong();
        } else if (tag == ProxyContent.FLOAT) {
            return readFloat();
        } else if (tag == ProxyContent.DOUBLE_32 || tag == ProxyContent.DOUBLE_64) {
            return readDouble();
        } else if (tag == ProxyContent.STRING || ProxyContent.STRING_SUB_TYPE.containsValue(tag)) {
            return readString();
        } else if (tag == ProxyContent.DATE) {
            return readDate();
        } else if (tag == ProxyContent.MAP || ProxyContent.MAP_SUB_TYPE.containsValue(tag)) {
            Map map = readMap();
            Integer structCode = MapUtil.getInt(map,STRUCT_CODE);
            if (structCode != null){
                DTO dto = StructUtil.create(structCode);
                return dto.forMap(map);
            }else {
                return map;
            }
        } else if (tag == ProxyContent.COLLECTION || ProxyContent.COLLECTION_SUB_TYPE.containsValue(tag)) {
            return readCollection();
        } else if (tag == ProxyContent.TRUE || tag == ProxyContent.FALSE) {
            return readBoolean();
        } else if (tag == ProxyContent.ISERIALIZABLE) {
            return readSerializable();
        } else if (tag == ProxyContent.ENUM) {
            return readEnum();
        } else {
            throw ArtifactExceptionUtil.error(ArtifactErrorCode.UNKNOWN_ERROR);
        }
    }

}
